# -*- coding:utf-8 -*-
import sublime, sublime_plugin
import os
import subprocess, threading
import time

hello_world = "#include <iostream>\n" + "using namespace std;\n" + "int main(){\n" + '\tcout << "hello_world\\n";\n' + "\treturn 0;\n}"

cmakelists = """cmake_minimum_required(VERSION 3.6)\n\nproject({0})\n\nset(CMAKE_CXX_STANDARD 11)\n\nset(CMAKE_CXX_FLAGS "-static-libgcc -static-libstdc++")\n\nadd_executable({0} main.cpp)"""


def mkdir(path):
	path = path.strip()
	path = path.rstrip("\\")
	isExists = os.path.exists(path)
	if not isExists:
		os.makedirs(path) 
		print(path + ' 创建成功')
		return True
	else:
		print(path +' 目录已存在')
		return False


def create_file(filename):
	path = filename[0:filename.rfind("/")]
	if not os.path.isdir(path):  # 无文件夹时创建
		os.makedirs(path)
	if not os.path.isfile(filename):  # 无文件时创建
		fd = open(filename, mode="w", encoding="utf-8")
		fd.close()


def write(file_path, content):
	with open(file_path, 'w') as file_object:
		file_object.write(content)
		file_object.close()

def get_sublime_text_exec_path():
	return sublime.executable_path()

def open_project(project_path):
	exec_path = get_sublime_text_exec_path()
	threading.Thread(target = lambda : subprocess.call([exec_path , project_path])).start()


def get_workspace():
		cwd = os.getcwd()
		workspace = cwd + "/Data/project"
		workspace = workspace.replace('\\', '/')
		return workspace

def build_cmake_cache(build_dir, callback):
	cmd = ["cmake" , "-DCMAKE_BUILD_TYPE=Debug", "-G", "CodeBlocks", "- MinGW Makefiles" , "../"]
	t = MessageProgress(sublime.active_window(), "building cmake cache", "build done!")
	t.start()
	if callback == None:
		callback = lambda : ()
	def task():
		subprocess.call(cmd, cwd=build_dir)
		callback()
		t.terminate()
	threading.Thread(target = task).start()
	
output = "" #存放每次子程序调用的输出结果

def build_project(build_path, window, callback):
	t = MessageProgress(window, "building target", "build done!")
	t.start()
	path =  build_path 
	cmd = ["cmake" , "--build", './']
	if callback == None:
		callback = lambda : ()
	def task():
		global output
		p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=build_path)
		def check_err():
			while p.poll() is None:
				time.sleep(1)
			print("check_err has err")
			p.kill()
		threading.Thread(target = check_err).start()
		try:
			output = p.stdout.read().decode()
			output += p.stderr.read().decode()
		except Exception as e:
			print(e)
		callback()
		p.kill()
		t.terminate()
		window.active_view().run_command("out_put")
	threading.Thread(target=task).start()


def reload_cmake(build_dir):
	cmd = ["cmake" , "-DCMAKE_BUILD_TYPE=Debug", "-G", "CodeBlocks", "- MinGW Makefiles" , "../"]
	t = MessageProgress(sublime.active_window(), "reloading cmake", "reload cmake!")
	t.start()
	def task():
		global output
		p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=build_dir)
		def check_err():
			while p.poll() is None:
				time.sleep(1)
			print("check_err has err")
			p.kill()
		threading.Thread(target = check_err).start()
		try:
			output = p.stdout.read().decode()
			output += p.stderr.read().decode()
		except Exception as e:
			print(e)
		t.terminate()
		sublime.active_window().active_view().run_command("out_put")
	threading.Thread(target = task).start()


def create_a_output_view():
	window = sublime.active_window()
	v = window.find_output_panel("output")
	if v != None:
		window.destroy_output_panel("output")
	v = window.create_output_panel("output")
	v.set_scratch(True)
	v.set_read_only(False)
	v.settings().set('word_wrap', True)
	window.run_command("show_panel", {"panel": "output.output"})
	return v


class OutPutCommand(sublime_plugin.TextCommand):
	def run(self, edit):
		global output
		output = output + "\n[finished]\n"
		v = create_a_output_view()
		v.insert(edit, v.size(), output)


class CmakeCommand(sublime_plugin.WindowCommand):
	options = ["init cmake project", "build project", "reload cmake", "build and run", "run"]
	build_sys = ["mingw"]
	pro_name = ""
	build_out_path = "/cmake-build-debug"
	
	def run(self):
		self.window.show_quick_panel(self.options, self.on_select, sublime.KEEP_OPEN_ON_FOCUS_LOST)


	def get_build_output_path(self):
		project_folders = self.window.folders()
		if len(project_folders) == 0:
			return None
		else:
			build_path = project_folders[0].replace('\\', '/')
			build_path = build_path +  self.build_out_path
			return build_path

	def build_project(self, callback):
		build_path = self.get_build_output_path()
		if not os.path.exists(build_path):
			sublime.error_message("Not a cmake project!")
			return
		build_project(build_path, self.window, callback)


	def reload_cmake(self):
		build_path = self.get_build_output_path()
		if not os.path.exists(build_path):
			sublime.error_message("Not a cmake project!")
			return
		reload_cmake(build_path)

	def run_target(self):
		platform = sublime.platform()
		if platform == "windows":
			project_folders = self.window.folders()
			if len(project_folders) == 0:
				sublime.error_message("No project to build!")
			else:
				build_path = project_folders[0].replace('\\', '/')
				build_path_split = build_path.split("/")
				pro_name = build_path_split[len(build_path_split) - 1]
				build_path = build_path +  self.build_out_path
				target = build_path + "/" + pro_name + ".exe"
				print("target:", target)
				if not os.path.exists(build_path):
					sublime.error_message("target can not find!")
					return
				cmd = [target]
				
				def task():
					global output
					p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, cwd=self.get_build_output_path())
					def check_err():
						while p.poll() is None:
							time.sleep(1)
						print("check_err has err")
						p.kill()
					threading.Thread(target = check_err).start()
					try:
						output = p.stdout.read().decode()
						output += p.stderr.read().decode()
					except Exception as e:
						print(e)
					sublime.active_window().active_view().run_command("out_put")
				threading.Thread(target = task).start()
		else:
			sublime.error_message("not yet support current system!")

	def build_and_run(self):
		build_path = self.get_build_output_path()
		print("build_path:", build_path)
		if build_path == None:
			sublime.error_message("get build_path err!")
			return
		if not os.path.exists(build_path):
			sublime.error_message("Not a cmake project!")
			return
		def block_run_target():
			platform = sublime.platform()
			if platform == "windows":
				project_folders = self.window.folders()
				if len(project_folders) == 0:
					sublime.error_message("No project to build!")
				else:
					build_path = project_folders[0].replace('\\', '/')
					build_path_split = build_path.split("/")
					pro_name = build_path_split[len(build_path_split) - 1]
					build_path = build_path +  self.build_out_path
					target = build_path + "/" + pro_name + ".exe"
					print("target:", target)
					if not os.path.exists(build_path):
						sublime.error_message("target can not find!")
						return
					cmd = [target]
					global output
					p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, cwd=self.get_build_output_path())
					try:
						output = p.stdout.read().decode()
						output += p.stderr.read().decode()
					except Exception as e:
						print(e)
		build_project(build_path, self.window, block_run_target)
				

	def on_select(self, idx):
		print("idx", idx)
		vs = self.window.views() #先保存所有view的内容
		for v in vs:
			v.run_command("save")

		if idx == 0:
			self.window.show_input_panel("project name:", "untitled", self.on_set_name_done, None, None)

		elif idx == 1:
			self.build_project(None)

		elif idx == 2:
			self.reload_cmake()

		elif idx == 3:
			self.build_and_run()

		elif idx == 4:
			self.run_target()

	def on_set_name_done(self, project_name):
		self.pro_name = project_name
		project_path = get_workspace() + "/" + self.pro_name
		isExists = os.path.exists(project_path)
		if isExists:
			sublime.error_message("project: " + self.pro_name + " is exists!")
			return
		self.on_build_sys_select()


	def on_build_sys_select(self):
		
		workspace = get_workspace()
		mkdir(workspace)
		project_path = workspace + "/" + self.pro_name

		mkdir(project_path)
		main_file = project_path + "/main.cpp"
		create_file(main_file)
		write(main_file, hello_world)

		cmake_lists_path = project_path + "/CMakelists.txt"
		create_file(cmake_lists_path)
		write(cmake_lists_path, cmakelists.format(self.pro_name))

		build_dir = project_path + self.build_out_path
		mkdir(build_dir)
		build_cmake_cache(build_dir, lambda : open_project(project_path))
		sublime.status_message("building project!")


class MessageProgress(threading.Thread):

	def __init__(self, window, message, done_message):
		super().__init__()
		self.window = window
		self.view = window.active_view()
		self.message = message
		self.done_message = done_message
		self.size = 10
		self.before = 0
		self.stop = False


	def run(self):
		while self.stop != True:
			self.before = (self.before + 1) % self.size
			after = self.size - self.before
			if self.view.is_valid():
				self.view = self.window.active_view()
			self.view.set_status('cmake','%s [%s>%s]' % (self.message, ' ' * self.before, ' ' * after))
			time.sleep(0.1)
		if self.stop:
			self.view.set_status('cmake', self.done_message)

	def terminate(self):
		self.stop = True

instance = CmakeCommand(sublime.active_window())

class BuildRunCommand(sublime_plugin.WindowCommand):
	def run(self):
		print(instance.window.folders())
		# instance.build_and_run()


code = "run-btn-instance-code"

class RunBtnCommand(sublime_plugin.WindowCommand):
	
	instance_name = code

	def run(self):
		current_view = self.window.active_view()
